/**
 * 集合：由一组无序且唯一的项组成
 * 运用于数据库，获取一个数据集合的查询语句相当于集合的运算
 */
export default class Set<T> {
    private _items: any;
    private _count: number;
    constructor() {
        this._items = {};
        this._count = 0;
    }
    /**
     * 元素是否在集合中
     */
    public has(element: T) {
        /*
            this._items.hasOwnProperty(element)，这个可能有问题，它继承自Object.prototype的hasOwnProperty
            这hasOwnProperty方法可能会被覆盖，而且并不是所有的对象都继承有Object.prototype，所以这里直接使用
             Object.prototype.hasOwnProperty和call
        */
        return Object.prototype.hasOwnProperty.call(this._items, element);
    }
    /**
     * 向集合添加一个新元素
     */
    public add(element: T): boolean {
        if (this.has(element)) { // 存在了就不用添加了
            return false;
        }
        this._items[element] = element;
        this._count ++;
        return true;
    }
    /**
     * 从集合移除一个元素
     */
    public delete(element: T): boolean {
        if (!this.has(element)) { // 不存在就不用移除
            return false;
        }
        delete this._items[element];
        this._count --;
        return true;
    }
    /**
     * 返回集合所包含元素的数量
     */
    public size() {
        return this._count;
    }
    /**
     * 集合是否为空
     */
    public isEmpty() {
        return this.size() === 0;
    }
    /**
     * 返回集合所包含元素的数量
     */
    public sizeLegacy(): number {
        return Object.keys(this._items).length; // es2015
        // let count = 0;
        // for (const key in this._items) {
        //     if (Object.prototype.hasOwnProperty.call(this._items, key)) {
        //         count ++;
        //     }
        // }
        // return count;
    }
    /**
     * 返回一个包含集合中所有值（元素）的数组
     */
    public values(): T[] {
        return Object.values(this._items); // es2017
    }
    /**
     * 返回一个包含集合中所有值（元素）的数组
     */
    public valuesLegacy(): T[] {
        const values: T[] = [];
        for (const key in this._items) {
            if (Object.prototype.hasOwnProperty.call(this._items, key)) {
                values.push(this._items[key]);
            }
        }
        return values;
    }
    /**
     * 移除集合中所有的元素
     */
    public clear() {
        this._items = {};
        this._count = 0;
    }
    /**
     * 返回集合的字符串形式
     */
    public toString(): string {
        if (this.isEmpty()) {
            return '';
        }
        const values = this.values();
        let objString: string =  `${values[0]}`;
        for (let i = 1; i < values.length; i ++) {
            objString = `${objString},${values[i].toString()}`;
        }
        return objString;
    }
    /**
     * 并集：对于给定的两个集合，返回一个包含两个集合中所有元素的新集合
     */
    public union(otherSet: Set<T>): Set<T> {
        const unionSet = new Set<T>();
        this.values().forEach(value => unionSet.add(value)); // es2015
        otherSet.values().forEach(value => unionSet.add(value)); // es2015
        return unionSet;
    }
    /**
     * 交集：对于给定的两个集合，返回一个包含两个集合中共有元素的新集合
     */
    public intersection(otherSet: Set<T>): Set<T> {
        const intersectionSet = new Set<T>();
        let smallerSet: Set<T> = otherSet; // 我们默认遍历otherSet，如果this更小就两个交换一下
        let biggerSet: Set<T> = this;
        if (otherSet.size() > this.size()) {
            smallerSet = this;
            biggerSet = otherSet;
        }
        smallerSet.values().forEach(value => {
            if (biggerSet.has(value)) {
                intersectionSet.add(value);
            }
        }); // es2015
        return intersectionSet;
    }
    /**
     * 差集：对于给定的两个集合，返回一个包含所有存在于第一个集合且不存在于第二个集合的元素的新集合
     * 存在于this而不存在与otherSet
     */
    public difference(otherSet: Set<T>): Set<T> {
        const differenceSet = new Set<T>();
        this.values().forEach(value => {
            if (!otherSet.has(value)) {
                differenceSet.add(value);
            }
        });
        return differenceSet;
    }
    /**
     * 子集：验证一个给定集合是否是另一个集合的子集
     */
    public isSubsetOf(otherSet: Set<T>): boolean {
        if (this.size() > otherSet.size()) {
            return false;
        }
        let isSubset: boolean = true;
        this.values().forEach(value => {
            if (!otherSet.has(value)) {
                isSubset = false;
            }
        });
        return isSubset;
    }
}
